package com.fhzn.auth.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fhzn.auth.constant.Constants;
import com.fhzn.auth.entity.*;
import com.fhzn.auth.enums.RoleUserBatchOpType;
import com.fhzn.auth.enums.UserTypeEnum;
import com.fhzn.auth.enums.WebResponseEnum;
import com.fhzn.auth.mapper.*;
import com.fhzn.auth.service.OperatorLogService;
import com.fhzn.auth.service.UserRoleService;
import com.fhzn.auth.service.UserService;
import com.fhzn.auth.tools.*;
import com.fhzn.auth.web.converter.UserConverter;
import com.fhzn.auth.web.request.*;
import com.fhzn.auth.web.vo.UserVO;
import com.fhzn.commons.toolkit.exception.BuzException;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 老顾
 * @apiNote
 * @since 2023/12/22
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final OperatorLogService operatorLogService;
    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    private final UserRoleService userRoleService;
    private final UserRoleMapper userRoleMapper;
    private final UserRelationMapper userRelationMapper;
    private final UserManager userManager;
    private final DataResourceMapper dataResourceMapper;
    private final UserDataResourceMapper userDataResourceMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void configureUsersRole(List<String> userNames, String roleCode, String handler, int type, String appCode) {

        List<User> users = userMapper.listUserByNickNames(userNames);

        if (CollectionUtils.isEmpty(users)) {
            throw new BuzException("用户不存在");
        }

        // 获取用户id列表
        List<Integer> userIds = users.stream().map(User::getId).collect(Collectors.toList());

        if (users.size() != userNames.size()) {
            userNames.removeAll(users.stream().map(User::getNickname).collect(Collectors.toList()));
            throw new BuzException(String.format("用户 %s 不存在", StringUtils.join(userNames, ",")));
        }

        // 根据code获取角色信息
        Role role = roleMapper.getRoleByCode(roleCode, appCode);
        if (role == null) {
            throw new BuzException("角色不存在");
        }

        // 根据用户昵称查询用户信息
        User handlerUser = userMapper.findUserByNickName(handler);
        // 根据用户昵称查询角色列表
        List<Role> handlerRoles = roleMapper.findRolesByNickname(handler);

        // 只有管理员，或者上级才可以分配
        distribution(handlerRoles, role, handlerUser, userIds);

        Integer roleId = role.getId();

        // 根据用户id和角色id查询用户角色关联列表
        List<UserRole> existedUserRoles = userRoleMapper.queryByUsersAndRole(userIds, roleId);
        List<Integer> existedUserIds = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(existedUserRoles)) {
            existedUserRoles.forEach(t -> existedUserIds.add(t.getUserId()));
        }

        List<UserRole> userRoles = Lists.newArrayList();

        // 分配角色
        assignRoles(handler, type, userIds, existedUserIds, roleId, userRoles);

    }

    @Override
    public Page<User> userWithPage(UserQueryRequest request) {
        request.checkParams();
        request.rewriteParams();
        request.getOffset();
        request.initQueryTime();

        return userMapper.query(request);
    }

    @Override
    public UserVO add(UserOpRequest request) {
        request.checkBaseParams();
        request.setUserType(1);
        request.checkAdd();
        if (StringUtils.isAnyBlank(request.getNickname())) {
            throw new BuzException("请求参数错误");
        }
        // 校验添加用户名的唯一性
        if (userMapper.findUserByNickName(request.getNickname()) != null) {
            throw new BuzException("当前用户名已存在");
        }
        User user = request.addGenServiceParam();
        this.addUser(user, request.getHandler());
        // 记录操作日志
        User userInfo = userMapper.findUserByNickName(request.getNickname());
        operatorLogService.operatorLog(request.getHandler(), Constants.UESR_SECTION, Constants.UESR_ADD_METHOD, request.getNickname(), null, userInfo, Constants.AUTH_APP_CODE);
        return UserConverter.fromUser(user);
    }

    @Override
    public String updateUser(UserOpRequest request) {
        request.checkBaseParams();
        request.setUserType(1);
        request.checkUpdate();

        User userBefore = userMapper.selectById(request.getId());
        this.updateUser(request.genServiceParam(), request.getHandler());

        // 记录操作日志
        operatorLogService.operatorLog(request.getHandler(), Constants.UESR_SECTION, Constants.UESR_UPDATE_METHOD, userBefore.getNickname(), userBefore, userBefore, Constants.AUTH_APP_CODE);

        return WebResponseEnum.SUCCESS.getMessage();
    }

    @Override
    public String addRole(UserRoleRequest request) {
        if (!request.getRoleIds().isEmpty()) {
            request.checkBaseParams();
            request.setOperatorUser(request.getHandler());
            request.setTargetUser(request.getTargetUser());
            request.setRoleIds(request.getRoleIds());
        }
        return userRoleService.addRole(request);
    }

    @Override
    public String passwordSearch(UserPasswordRequest request) {
        // 校验用户
        request.checkParams();
        User user = userMapper.findUserByNickName(request.getNickname());
        if (user == null) {
            throw new BuzException("输入的用户名有误");
        }
        if (Constants.DISABLE == user.getStatus()) {
            throw new BuzException("当前用户被禁用，不允许进行密码查询");
        }
        if (Constants.PASSWORD_NOT_EXPIRED == user.getPasswordExpired()) {
            throw new BuzException("当前密码不允许查询，请联系管理员重置密码");
        }
        // 记录操作日志
        operatorLogService.operatorLog(request.getHandler(), Constants.UESR_SECTION, Constants.USER_PASSWORD_SEARCH_LOG, request.getNickname(), null, null, Constants.AUTH_APP_CODE);
        return AESEncryptorUtils.decrypt(user.getPassword(), user.getSalt());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String resetPassword(UserPasswordRequest request) {
        request.checkBaseParams();
        request.checkParams();
        // 校验外包用户
        User user = userMapper.findUserByNickName(request.getNickname());
        if (user == null) {
            throw new BuzException("输入的用户名有误");
        }
        if (user.getStatus() == Constants.DISABLE) {
            throw new BuzException("当前用户被禁用，不允许进行密码重置");
        }
        // 重置密码
        user.setModifier(request.getHandler());
        this.resetPassword(user);

        // 记录操作日志
        operatorLogService.operatorLog(request.getHandler(), Constants.UESR_SECTION, Constants.USER_PASSWORD_RESET_LOG, request.getNickname(), null, AESEncryptorUtils.decrypt(user.getPassword(), user.getSalt()), Constants.AUTH_APP_CODE);
        return WebResponseEnum.SUCCESS.getMessage();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String passwordModify(UserPasswordRequest request) {
        request.checkParams();
        request.checkBaseParams();
        request.setNickname(request.getNickname());
        // 校验数据
        if (StringUtils.isEmpty(request.getMpass()) || StringUtils.isEmpty(request.getNewpass())) {
            throw new BuzException("当前用户密码为空");
        }
        if (StringUtils.equals(request.getMpass(), request.getNewpass())) {
            throw new BuzException("当前用户输入原密码和新密码相同");
        }
        // 校验用户
        User user = userMapper.findUserByNickName(request.getNickname());
        if (user == null) {
            throw new BuzException("当前用户不存在");
        }
        if (user.getStatus() == Constants.DISABLE) {
            throw new BuzException("当前用户账户被禁用");
        }

        // 1.超期用户,指的是用户第一次系统生成的密码,生成规则和后续修改的密码有所不同
        if (user.getPasswordExpired() == Constants.PASSWORD_EXPIRED) {
            try {
                request.setMpass(StandardRSAUtils.decryptByPrivateKey(request.getMpass(), Constants.RSA_PRI_KEY));
            } catch (Exception e) {
                throw new BuzException("原始密码有误");
            }
            try {
                request.setNewpass(StandardRSAUtils.decryptByPrivateKey(request.getNewpass(), Constants.RSA_PRI_KEY));
                request.setRenewpass(request.getNewpass());
            } catch (Exception e) {
                throw new BuzException("新密码数据有误");
            }
            this.passwordModification(request.getNewpass(), user);
        } else {
            // 2.未超期用户
            if (!StringUtils.equals(Md5HashUtil.toMd5(request.getMpass() + user.getSalt() + Constants.MD5_SECRET_KEY), user.getPassword())) {
                throw new BuzException("原始密码有误");
            }
            this.passwordModification(request.getNewpass(), user);
        }
        // 记录操作日志,只有密码修改成功才会记录操作日志
        operatorLogService.operatorLog(request.getHandler(), Constants.UESR_SECTION, Constants.USER_PASSWORD_MODIFY_LOG, request.getNickname(), request.getMpass(), request.getNewpass(), Constants.AUTH_APP_CODE);
        return WebResponseEnum.SUCCESS.getMessage();
    }

    @Override
    public String passwordCheck(UsernamePasswordRequest request) {
        if (StringUtils.isAnyBlank(request.getUsername(), request.getPassword())) {
            throw new BuzException("请求参数错误，请检查后重试");
        }
        String password;
        try {
            password = StandardRSAUtils.decryptByPrivateKey(request.getPassword(), Constants.RSA_PRI_KEY);
        } catch (Exception e) {
            throw new BuzException("密码解密失败");
        }
        return this.checkUsernamePassword(request.getUsername(), password);
    }

    @Override
    public User findUserByNickName(String nickname) {
        if (StringUtils.isBlank(nickname)) {
            throw new BuzException("用户" + nickname + "不存在");
        }
        return userMapper.findUserByNickName(nickname);
    }

    @Override
    public List<DataResource> getUserDataResByParentCode(DataResourceRequest request) {
        request.checkBaseParams();
        if (StringUtils.isAnyBlank(request.getHandler(), request.getInvokeAppName(), request.getParentCode())) {
            return Collections.emptyList();
        }
        this.findUserByNickName(request.getHandler());
        List<Integer> dataResourceIds = userDataResourceMapper.getUsersByNickname(request.getHandler());
        return dataResourceMapper.findUserDataResByNicknameAndParentCode(dataResourceIds, request.getInvokeAppName(), request.getParentCode());
    }

    /**
     * 校验用户名和密码
     *
     * @param userName 用户名
     * @param password 密码
     */
    private String checkUsernamePassword(String userName, String password) {
        try {
            User user = userMapper.findUserByNickName(userName);
            if (null == user) {
                throw new BuzException("用户不存在");
            }
            if (!StringUtils.equals(userName, user.getNickname())) {
                throw new BuzException("用户名错误");
            }
            if (Constants.DISABLE == user.getStatus()) {
                throw new BuzException("当前用户账号已被禁用");
            }
            String salt = user.getSalt();
            Integer passwordExpired = user.getPasswordExpired();
            String passwordInfo = user.getPassword();
            if (StringUtils.isAnyBlank(salt, password, passwordInfo) || null == passwordExpired) {
                throw new BuzException("当前用户账号信息有异常，请联系管理员处理");
            }
            if (Constants.PASSWORD_EXPIRED == passwordExpired) {
                final String digestedPassword = AESEncryptorUtils.encrypt(password, salt);
                if (!passwordInfo.equalsIgnoreCase(digestedPassword)) {
                    throw new BuzException("用户账号或密码错误");
                }
            } else if (Constants.PASSWORD_NOT_EXPIRED == passwordExpired) {
                final String digestedPassword = Md5HashUtil.toMd5(password + salt + "wacaikuaidai");
                if (!passwordInfo.equalsIgnoreCase(digestedPassword)) {
                    throw new BuzException("用户账号或密码错误");
                }
            } else {
                throw new BuzException("当前用户账号信息有异常，请联系管理员处理");
            }
        } catch (Exception e) {
            throw new BuzException("服务器开小差，请稍后再试");
        }
        return "用户 " + userName + " 认证成功";
    }

    /**
     * 修改密码
     *
     * @param newpass 新密码
     * @param user    用户信息
     */
    private void passwordModification(String newpass, User user) {
        user.setPasswordExpired(Constants.PASSWORD_NOT_EXPIRED);
        user.setPassword(Md5HashUtil.toMd5(newpass + user.getSalt() + Constants.MD5_SECRET_KEY));
        user.setModifier(user.getNickname());
        user.setUpdatedTime(new Date());
        userMapper.updateUserInfo(user);
    }

    /**
     * 重置密码
     *
     * @param user 用户信息
     */
    private void resetPassword(User user) {
        String salt = RandomUtil.getRandomLetterAndNumberAndSymbol(AESEncryptorUtils.SALT_SIZE);
        user.setSalt(salt);
        user.setPasswordExpired(Constants.PASSWORD_EXPIRED);
        // 去掉易混淆的字母
        user.setPassword(AESEncryptorUtils.encrypt(RandomUtil.getSimpleLetterNumberSymbol(8), salt));
        // 启用
        user.setStatus(Constants.ENABLE);
        userMapper.resetPassword(user);
    }

    /**
     * 更新用户信息
     *
     * @param user    用户信息
     * @param handler 用户昵称
     */
    private void updateUser(User user, String handler) {
        List<Role> roles = roleMapper.findRolesByNickname(handler);
        User userInfo = userMapper.selectById(user.getId());
        if (userInfo.getNickname().equals(handler) && !AuthUtil.isSuperAdmin(roles)) {
            throw new BuzException("没有权限修改自己");
        }
        if (!userInfo.getStatus().equals(user.getStatus()) && userManager.canOperatorUser(handler)) {
            throw new BuzException("只有超级管理员以及员工录入人员才能修改用户状态");
        }
        if (!userInfo.getUserType().equals(user.getUserType())) {
            throw new BuzException("用户类型无法修改");
        }
        User handlerUser = userMapper.findUserByNickName(handler);
        List<UserRelation> relations = userRelationMapper.findRelations(handlerUser.getId(), user.getId());
        if (AuthUtil.isAdminOrSuperAdmin(roles) || !relations.isEmpty()) {
            String userSn = user.getSn();
            if (!validate(userSn)) {
                user.setSn(null);
            }
            userMapper.updateById(user);
            if (userInfo.getUserType() == 1) {
                return;
            }
            if (!validate(userSn)) {
                throw new BuzException("动态口令sn格式不正确");
            }

        } else {
            throw new BuzException("无权限操作");
        }
    }

    /**
     * 添加用户
     *
     * @param user    用户信息
     * @param handler 用户昵称
     */
    private void addUser(User user, String handler) {
        if (userManager.canOperatorUser(handler)) {
            throw new BuzException("没有添加对应员工的权限");
        }
        userMapper.insert(user);
        if (UserTypeEnum.isInnerUser(user.getUserType())) {
            userManager.addDefaultRole(user.getId(), handler);
        }

        if (UserTypeEnum.isOuterUser(user.getUserType())) {
            // 外部用户才会使用sn码
            String userSn = user.getSn();
            if (!validate(userSn)) {
                throw new BuzException("动态口令sn格式不正确");
            }
        }
    }


    /**
     * 校验动态口令sn码
     *
     * @param sn sn码
     * @return true/false
     */
    private static Boolean validate(String sn) {
        return (StringUtils.isNotBlank(sn) && StringUtils.isNumeric(sn.trim()) && sn.trim().length() == 9) || StringUtils.isBlank(sn);
    }

    /**
     * 分配角色
     *
     * @param handler        操作员
     * @param type           操作类型
     * @param userIds        用户id列表
     * @param existedUserIds 已存在用户id列表
     * @param roleId         角色id
     * @param userRoles      用户角色列表
     */
    private void assignRoles(String handler, int type, List<Integer> userIds, List<Integer> existedUserIds, Integer roleId, List<UserRole> userRoles) {
        if (type == RoleUserBatchOpType.ADD.getType()) {
            userIds.stream().filter(r -> !existedUserIds.contains(r)).forEach(t -> {
                UserRole userRole = new UserRole();
                userRole.setUserId(t);
                userRole.setRoleId(roleId);
                userRole.setCreator(handler);
                userRoles.add(userRole);
            });
            if (CollectionUtils.isEmpty(userRoles)) {
                throw new BuzException("角色批量分配用户失败, 已存在该用户绑定的角色");
            }
            // 批量插入用户角色关联信息
            userRoleService.saveBatch(userRoles);
            // 记录操作日志
            userRoles.forEach(userRole -> operatorLogService.operatorLog(handler, Constants.ROLE_SECTION, Constants.ROLE_ADD_USER_METHOD, userRole.getUserId(), "", roleId, Constants.AUTH_APP_CODE));
        } else if (type == RoleUserBatchOpType.REMOVE.getType()) {
            userIds.stream().filter(existedUserIds::contains).forEach(t -> {
                UserRole userRole = new UserRole();
                userRole.setUserId(t);
                userRole.setRoleId(roleId);
                userRole.setCreator(handler);
                userRoles.add(userRole);
            });

            if (CollectionUtils.isEmpty(userRoles)) {
                return;
            }
            userRoleMapper.deleteBatchByIds(userRoles);
            userRoles.forEach(userRole -> {
                operatorLogService.operatorLog(handler, Constants.ROLE_SECTION, Constants.ROLE_DEL_USER_METHOD, userRole.getUserId(), roleId, 0, Constants.AUTH_APP_CODE);
            });
        } else {
            throw new BuzException("不支持的操作");
        }
    }

    /**
     * 分配上下级关系
     *
     * @param handlerRoles 操作员角色列表
     * @param role         角色
     * @param handlerUser  操作员
     * @param userIds      用户id列表
     */
    private void distribution(List<Role> handlerRoles, Role role, User handlerUser, List<Integer> userIds) {
        if (!AuthUtil.isAdminOrSuperAdmin(handlerRoles, role.getAppCode())) {

            List<UserRelation> relations = userRelationMapper.findRelationsWithUsers(handlerUser.getId(), userIds);

            relations = relations.stream().filter(t -> t.getAppCode().equals(role.getAppCode())).collect(Collectors.toList());

            if (CollectionUtils.isEmpty(relations)) {
                throw new BuzException("不是上级或管理员, 无操作权限");
            }

            List<Integer> juniorIds = relations.stream().map(UserRelation::getUserId).collect(Collectors.toList());

            if (juniorIds.size() != userIds.size()) {
                throw new BuzException("无操作权限, 请检查上下级关系");
            }

            List<Role> canAppointRoles = roleMapper.findCanAppointRoles(handlerRoles.stream().map(Role::getId).collect(Collectors.toList()));

            if (!canAppointRoles.contains(role)) {
                throw new BuzException("无操作权限, 没有分配该角色的权限");
            }
        }
    }
}
